home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Tool Chest / Files / XTND 1.3.6 / Application Examples / CSource / TE_FileIO.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-06  |  33.9 KB  |  1,049 lines  |  [TEXT/MPS ]

  1. /************************************************************************
  2. *                                                                        *
  3. *    TE_FileIO.c                                                            *
  4. *                                                                        *
  5. *    XTND import and export routines for TESample.                        *
  6. *                                                                        *
  7. *    Copyright © 1988 Claris Corporation                                    *
  8. *    All Rights Reserved                                                    *
  9. *                                                                        *
  10. ************************************************************************/
  11.  
  12.  
  13. #ifdef THINK_C
  14.     #define const        /* when is ThinkC going to get with the program? */
  15. #else
  16.     #pragma load "MacIncludes"
  17. #endif
  18.  
  19. #ifndef __PACKAGES__
  20. #include <Packages.h>
  21. #endif
  22.  
  23. #include "TESample.h"
  24. #include ":::XTND Headers:XTNDCIncludes:XTNDInterface.h"
  25. #include ":::XTND Headers:XTNDCIncludes:XTNDTextTranslator.h"
  26.  
  27. /*------------------------- Useful Constants ---------------------------*/
  28.  
  29. #define QUICKDRAWSTYLES        (bold+italic+underline+outline+shadow)
  30. #define    STRINGS    25002
  31. #define    NUMSTDS    3
  32. #define FALSE     0
  33. #define TRUE    1
  34. #define STD_FILETYPE(iType)    (((iType) == 0) ? 'MOOD': ((iType) == 1) ? 'MOOS' : 'TEXT')
  35.  
  36.  
  37. /*-------------------------Function prototypes--------------------------*/
  38.  
  39. /*----------External Functions----------*/
  40. extern    short    gNumDocuments;
  41. extern    void    DoNew(void);
  42. extern    void    AlertUser( short, short );
  43. extern    void    DoZoomWindow(WindowPtr, short);
  44. extern     void     AdjustScrollValues( WindowPtr window, Boolean canRedraw );
  45.  
  46. /*----------Externally visible functions in this file----------*/
  47. void    XTNDInit(void);
  48. void    DoOpen(void);
  49. void    DoSave(Boolean);
  50.  
  51. /*----------Static functions in this file----------*/
  52. static void ReadFile(TransDescribe *, SFReply);
  53. static OSErr ReadPlainTextFile(const SFReply *pTheReply, TEHandle hTE);
  54. static void SaveFile(TransDescribe *, SFReply);
  55. static OSErr SavePlainTextFile(Str255 fileName, short vRefNum, long dirID,
  56.                 TEHandle hTE, Boolean saveAll);
  57. static OSErr SaveNewPlainTextFile(const SFReply *pTheReply, OSType fileType,
  58.                 TEHandle hTE, Boolean saveAll, short *pVRefNum, long *pDirID);
  59. static void Getnextstory(ImportParmBlkPtr);
  60. static void RGBFromXTND(RGBColor *, short);
  61. static short RGBToXTND(RGBColor);
  62. static void ZeroBlock(register void *, register long);
  63.  
  64. /*-------------------------Global Variables-----------------------------*/
  65.  
  66. static    short        Load_stored = 1;        /* Selected translator in GetFile popup menu (initially "All Available") */
  67. static    short        Save_stored = 1;        /* Selected translator in PutFile popup menu */
  68. static    Boolean        gXTNDLibOK    = FALSE;    /* Indicates whether XTND initialized successfully or not */
  69.  
  70. extern    CursHandle    WATCH;                    /* Watch Cursor */
  71.  
  72. TransProcPtr    gImportTranslator;            /* Pointer to code of Import Translator */
  73. TransProcPtr    gExportTranslator;            /* Pointer to code of Export Translator */
  74.  
  75. TransDescribe    Standards[NUMSTDS];            /* Array of types that this application can read */
  76.  
  77.  
  78. /*----------------------------------------------------------------------*/
  79. /* XTNDInit initializes the XTND Library and sets the global flag         */
  80. /*  gXTNDLibOK to TRUE if the library was successfully initialized.        */
  81. /*  Also sets up an array of translator descriptor records which         */
  82. /*  describe the files our application is able to read or write without    */
  83. /*  the need of external translators. This global array is used when     */
  84. /*  calling the XTND Library routines XTNDGetFile() and XTNDPutFile().     */
  85. /*----------------------------------------------------------------------*/
  86. void XTNDInit(void)
  87. {
  88.     register long iStd;
  89.     Str255 XTNDSystemName, ClarisFolderName;
  90.  
  91.     /* one-time initialization of the XTND Library… */
  92.     GetIndString(XTNDSystemName, xtndNames, xtndSystem);
  93.     GetIndString(ClarisFolderName, clarisNames, clarisFolder);
  94.  
  95.     gXTNDLibOK = (XTNDInitTranslators(kTransVersion, XTNDSystemName, ClarisFolderName) == noErr);
  96.     
  97.     /* 
  98.     The application should really check here for an error from InitTranslators 
  99.     - it may need to report something to the user
  100.     */
  101.     
  102.     for (iStd = 0; iStd < NUMSTDS; iStd++)
  103.     {
  104.         Standards[iStd].version = 2;
  105.         Standards[iStd].translatorType = 'FLTI';
  106.         Standards[iStd].codeResID = 0;
  107.         Standards[iStd].FDIFResID = -1;
  108.         Standards[iStd].numVersBytes = 0;
  109.         Standards[iStd].unused1 = 0;
  110.         Standards[iStd].pathLength = 0;
  111.         Standards[iStd].flags = 0;
  112.         Standards[iStd].numMatches = 1;
  113.         Standards[iStd].matches[0].docCreator = 'XTND';
  114.         Standards[iStd].matches[0].docType = STD_FILETYPE(iStd);
  115.         Standards[iStd].matches[0].exactMatch = FALSE;
  116.         GetIndString(Standards[iStd].name, STRINGS, iStd + 1);
  117.     }
  118. }
  119.  
  120.  
  121. /*----------------------------------------------------------------------*/
  122. /* DoOpen is the handler for the open command. Prompts the user for     */
  123. /*  the file to open. If the user selects a file a new document window    */
  124. /*  is created and the file is read in. If the XTND Library was         */
  125. /*  successfully initialized its XTNDGetFile() routine is used to get     */
  126. /*  the user’s document selection, otherwise the Standard File             */
  127. /*  SFGetFile() routine is used.                                         */
  128. /*----------------------------------------------------------------------*/
  129. void DoOpen(void)
  130. {
  131.     Boolean getIt;
  132.     SFReply myReply;
  133.     SFParamBlock myXSFPB;
  134.  
  135.     if (gXTNDLibOK) {
  136.         ZeroBlock(&myXSFPB,sizeof(SFParamBlock));
  137.         myXSFPB.allowFlags = allowText;
  138.         myXSFPB.numStandard = NUMSTDS;
  139.         myXSFPB.standard = Standards;
  140.         myXSFPB.ioResult = 0;
  141.         myXSFPB.fileReply = &myReply;
  142.         myXSFPB.XTNDDlogHook = nil; /* (XTNDDlgHookProcPtr) MyDlg; */
  143.         myXSFPB.currentMenuItem = Load_stored;
  144.         myXSFPB.where.v = myXSFPB.where.h = 0;
  145.         myXSFPB.prompt = (unsigned char *)"\pSelect a file to open";
  146.         myXSFPB.buttonTitle = (unsigned char *)"\pOpen";
  147.         myXSFPB.dialogID = 0;
  148.         myXSFPB.SFFilterProc = nil;
  149.         myXSFPB.showAllFiles = FALSE; 
  150.         myXSFPB.useTransList = FALSE;
  151.         myXSFPB.myFileFilter = nil;
  152.         myReply.good = TRUE;
  153.         getIt = XTNDGetFile(&myXSFPB);
  154.         Load_stored = myXSFPB.currentMenuItem;    
  155.     } else {
  156.         Point where = {0x40, 0x40};
  157.         SFTypeList myTypes = {'TEXT'};
  158.  
  159.         SFGetFile(where, NULL, NULL, 1, myTypes, NULL, &myReply);
  160.         getIt = myReply.good;
  161.     }
  162.     if (getIt) {
  163.         short numDocuments = gNumDocuments;
  164.  
  165.         DoNew();
  166.         if (numDocuments != gNumDocuments) {    /* Did we open a new window ? */
  167.             if (gXTNDLibOK && myXSFPB.chosenTranslator > myXSFPB.numStandard)
  168.                 ReadFile(myXSFPB.theChosenTranslator, myReply);    /* Read the file in using XTND. */
  169.             else {
  170.                 /* Use the appropriate internally supported method of reading the file.
  171.                    While our application claims to support three (NUMSTDS) formats, they are
  172.                    all actually simple TEXT documents. */
  173.                 (void)ReadPlainTextFile(&myReply, ((DocumentPeek)FrontWindow())->docTE);
  174.             }
  175.         }
  176.     }
  177. }
  178.  
  179.  
  180. /*----------------------------------------------------------------------*/
  181. /* DoSave is the handler for the Save and SaveAs commands. The             */
  182. /*  parameter saveAs specifies the Save As command when it is TRUE.     */
  183. /*  When handling a SaveAs command the user is prompted for a filename    */
  184. /*  and location to save the document. The window title of the             */
  185. /*  frontmost window is used as a default filename. If the user         */
  186. /*  specifies a filename and location the file is created (deleting     */
  187. /*  any previously existing one) and the contents of the frontmost         */
  188. /*  document window are written to it. If the XTND Library was             */
  189. /*  successfully initialized its XTNDPutFile() routine is used to get     */
  190. /*  the user’s document selection, otherwise the Standard File             */
  191. /*  SFGetFile() routine is used. Handling of the Save command is         */
  192. /*  currently not implemented. If DoSave() is called with saveAs FALSE     */
  193. /*  it will simply beep.                                                 */
  194. /*----------------------------------------------------------------------*/
  195. void DoSave(Boolean saveAs)
  196. {
  197.     Str255 wTitle;
  198.     Boolean putIt;
  199.     SFReply myReply;
  200.     SFParamBlock myXSFPB;
  201.     register WindowPtr window = FrontWindow();
  202.  
  203.     GetWTitle(window, wTitle);
  204.     if (!saveAs) {
  205.         /* Handle a simple Save routine here. */
  206.         SysBeep(1);
  207.         return;
  208.     }
  209.     if (gXTNDLibOK) {
  210.         ZeroBlock(&myXSFPB,sizeof(SFParamBlock));
  211.         myXSFPB.allowFlags = allowText + allowExport;
  212.         myXSFPB.numStandard = NUMSTDS;
  213.         myXSFPB.standard = Standards;
  214.         myXSFPB.ioResult = 0;
  215.         myXSFPB.fileReply = &myReply;
  216.         myXSFPB.applicNativeType = 'TEXT';
  217.         myXSFPB.XTNDDlogHook = nil; /* (XTNDDlgHookProcPtr) MyDlg; */
  218.         myXSFPB.currentSaveItem = Save_stored;
  219.         myXSFPB.where.v = myXSFPB.where.h = 0;
  220.         myXSFPB.prompt = (unsigned char *)"\pExport File";
  221.         myXSFPB.buttonTitle = (unsigned char *)"\pSave";
  222.         myXSFPB.origName = wTitle;
  223.         myXSFPB.dialogID = 0;
  224.         myXSFPB.SFFilterProc = nil;
  225.         myXSFPB.useTransList = FALSE;
  226.         myXSFPB.myFileFilter = nil;
  227.         myReply.good = TRUE;
  228.         putIt = XTNDPutFile(&myXSFPB);
  229.         Save_stored = myXSFPB.currentSaveItem;
  230.     } else {
  231.         Point where = {0x40, 0x40};
  232.         Str255 myPrompt = "\pSave document as:";
  233.  
  234.         SFPutFile(where, myPrompt, wTitle, NULL, &myReply);
  235.         putIt = myReply.good;
  236.     }
  237.     if (putIt) {
  238.         if (gXTNDLibOK && myXSFPB.chosenTranslator > myXSFPB.numStandard)
  239.             SaveFile(myXSFPB.theChosenTranslator, myReply);    /* Save the file using XTND. */
  240.         else {
  241.             long dirID;
  242.             short vRefNum;
  243.  
  244.             /* Use the appropriate internally supported method of saving the file.
  245.                While our application claims to support three (NUMSTDS) formats, they are
  246.                all actually simple TEXT documents. The only differentiation we make is
  247.                the fileType we create the documents as. */
  248.             (void)SaveNewPlainTextFile(&myReply, STD_FILETYPE(myXSFPB.chosenTranslator),
  249.                 ((DocumentPeek)FrontWindow())->docTE, /*saveAll*/ TRUE, &vRefNum, &dirID);
  250.         }
  251.     }
  252. }
  253.  
  254.  
  255. /*----------------------------------------------------------------------*/
  256. /* ReadFile reads the file in using XTND. It loads the translator,         */
  257. /*  initializes the import parameter block, and sends commands to the    */
  258. /*  translator to read in the document.  When it is finished, it        */
  259. /*  releases the translator.                                            */
  260. /*----------------------------------------------------------------------*/
  261. static    void    ReadFile(pChosenOne, theReply)
  262. register    TransDescribe    *pChosenOne;
  263.             SFReply            theReply;
  264. {
  265.     register    WindowPtr    window = FrontWindow();
  266.     register    Ptr            dummyptr;
  267.     register    long        TESlop = sizeof(TextStyle)+500L;
  268.     
  269.     PictMisc            **pm;
  270.     ImportParmBlock        importPB;
  271.     ParamBlockRec        hfsPB;
  272.     TEHandle            te;
  273.     Fixed                Parafmt[9];
  274.     TabSpec                Tabs[20];
  275.     Point                MinusOne;
  276.     Rect                tempRect;
  277.     Str255                Buffer;
  278.     unsigned char        Marker[10];
  279.     short                fnum = 0, resfnum = 0, tempErr, fserr, *aPtr;
  280.     long                count, textrun = 0L;
  281.     TextStyle            newStyle;
  282.     Str255                theNumber;
  283.     unsigned long        now;
  284.  
  285.     SetCursor(*WATCH);
  286.  
  287.     SetWTitle(window, theReply.fName);
  288.  
  289.     if (fserr = XTNDLoadTranslator(pChosenOne, &gImportTranslator)) {
  290.         AlertUser(eTranslatorLoad,fserr);
  291.         return;
  292.     }
  293.  
  294.     MinusOne.h = MinusOne.v = -1;
  295.     te = ((DocumentPeek) window)->docTE;
  296.     importPB.textBuffer        = (Ptr)Buffer;
  297.     importPB.result            = noErr;
  298.     importPB.textLength        = 0;
  299.     importPB.txtFace            = 0;    /* Plain */
  300.     importPB.txtSize            = 0;
  301.     importPB.txtFont            = helvetica;
  302.     importPB.txtColor            = 0;
  303.     importPB.txtJust            = 0;    /* Left */
  304.     importPB.paraFmts            = Parafmt;
  305.     importPB.tabs                = Tabs;
  306.     importPB.numCols            = 1;
  307.     importPB.currentStory        = mainStory;
  308.     importPB.miscData            = 0;
  309.     importPB.storyHeight        = 0;
  310.     importPB.decimalChar        = '.';
  311.     importPB.autoHyphenate        = TRUE;
  312.     importPB.printRecord        = NULL;
  313.     importPB.startPageNum        = 1;
  314.     importPB.startFootnoteNum    = 1;
  315.     importPB.footnoteText        = Marker;
  316.     importPB.footnoteText[0]    = 0;
  317.     importPB.rulerShowing        = 2;
  318.     importPB.doubleSided        = FALSE;
  319.     importPB.titlePage            = FALSE;
  320.     importPB.endnotes            = FALSE;
  321.     importPB.showInvisibles    = FALSE;
  322.     importPB.showPageGuides    = TRUE;
  323.     importPB.showPictures        = TRUE;
  324.     importPB.autoFootnotes        = TRUE;
  325.     importPB.pagePoint            = MinusOne;
  326.     importPB.datePoint            = MinusOne;
  327.     importPB.timePoint            = MinusOne;
  328.     importPB.smartQuotes        = TRUE;
  329.     importPB.fractCharWidths    = FALSE;
  330.     importPB.hRes                = 72;
  331.     importPB.vRes                = 72;
  332.     importPB.theReply            = theReply;
  333.     importPB.thisTranslator    = *pChosenOne;
  334.         
  335.     if (OpenRFPerm(theReply.fName, theReply.vRefNum, fsRdPerm) == -1)
  336.     {
  337.         if ((tempErr = ResError()) != eofErr) {             /* No resource fork found */
  338.             AlertUser(eResFailed,tempErr);
  339.             (void)XTNDReleaseTranslator(pChosenOne);
  340.             return;
  341.         }
  342.         UseResFile(pChosenOne->resRefNum);        /* For translators expecting to be the current resource file */
  343.     } else {     /* If there is a resource fork for this file, read the resources */    
  344.         resfnum = CurResFile();
  345.         importPB.refNum = resfnum;
  346.         importPB.directive = importGetResources;
  347.         (*gImportTranslator)(&importPB);
  348.         if (importPB.result)
  349.         {
  350.             AlertUser(eResReadFail,importPB.result);
  351.             CloseResFile(resfnum);
  352.             (void)XTNDReleaseTranslator(pChosenOne);
  353.             return;
  354.         }
  355.     }
  356.  
  357.     /* Open the file read only */
  358.     
  359.     fserr = 0;
  360.     hfsPB.ioParam.ioNamePtr = theReply.fName;
  361.     hfsPB.ioParam.ioVRefNum = theReply.vRefNum;
  362.     hfsPB.ioParam.ioVersNum = 1;
  363.     hfsPB.ioParam.ioPermssn = fsRdPerm;
  364.     hfsPB.ioParam.ioMisc = 0L;
  365.     
  366.     fserr = PBOpen(&hfsPB, FALSE);
  367.     if (fserr) {
  368.         AlertUser(eDocOpenFail,fserr);
  369.         CloseResFile(resfnum);
  370.         (void)XTNDReleaseTranslator(pChosenOne);
  371.         return;
  372.     }
  373.  
  374.     importPB.refNum = fnum = hfsPB.ioParam.ioRefNum;
  375.     importPB.directive = importInitAll;
  376.     (*gImportTranslator)(&importPB);
  377.     
  378.     /* After completing the initialization, check for an error.  If none, proceed. */
  379.     
  380.     if (importPB.result) {
  381.         AlertUser(eDocReadFail,importPB.result);
  382.         CloseResFile(resfnum);
  383.         (void)XTNDReleaseTranslator(pChosenOne);
  384.         return;
  385.     }
  386.     
  387.     /* STAGE ONE - just read in the TEXT of the file.  Ignore pictures */
  388.     
  389.     /* Set starting place to be the MAIN body of text. */
  390.     importPB.directive = importInitMain;
  391.     importPB.currentStory = mainStory;
  392.     (*gImportTranslator) (&importPB);
  393.     if (!importPB.result) {
  394.     
  395.         SetRect(&tempRect, 0, 0, 0, 0);
  396.         ClipRect(&tempRect);                        /* close clip rect so text will not be drawn */
  397.         GetDateTime(&now);
  398.         while (textrun < 30000)
  399.         {
  400.         
  401.             importPB.directive = importGetText;
  402.             (*gImportTranslator) (&importPB);
  403.             
  404.             fserr = importPB.result;
  405.             count = importPB.textLength;
  406.     
  407.             if (fserr || ((importPB.directive == importAcknowledge) && (count <= 0)))
  408.                 break;
  409.             
  410.             if (count == 1)    /* Is it a special character ? */
  411.             {
  412.                 if (Buffer[0] < 32)
  413.                     switch (Buffer[0])
  414.                     {
  415.                         case    2:    /* Page Number */
  416.                         case    3:    /* Footnote reference */
  417.                         case    5:    /* Footnote reference */
  418.                         case    6:    /* Merge Break Char */
  419.                         case    9:    /* Tab */
  420.                         case    11:    /* Column Break */
  421.                         case    12:    /* Page Break */
  422.                         case    31:    /* Discretionary Hyphen */
  423.                             count = 0;
  424.                             break;
  425.                             
  426.                         case    4:    /* Picture */
  427.                             /* We have to dispose of the picture, even if we don't use it. */
  428.                             pm = (PictMisc **) importPB.miscData;
  429.                             DisposHandle( (Handle) (**pm).thePicture);
  430.                             DisposHandle( (Handle) pm);
  431.                             count = 0;
  432.                             break;
  433.     
  434.                         case    21:    /* Short Date */
  435.                         case    22:    /* Abbrev Date */
  436.                         case    23:    /* Long date */
  437.                         case    24:    /* Abbrev + day Date */
  438.                         case    25:    /* Long + day Date */
  439.                             if (importPB.miscData)
  440.                                 IUDateString(importPB.miscData, shortDate, theNumber);
  441.                             else
  442.                                 IUDateString(now, shortDate, theNumber);
  443.                             count = theNumber[0];
  444.                             BlockMove(&(theNumber[1]), Buffer, count);
  445.                             break;
  446.                             
  447.                         case    26:    /* Time */
  448.                             if (importPB.miscData)
  449.                                 IUTimeString(importPB.miscData, FALSE, theNumber);
  450.                             else
  451.                                 IUTimeString(now, FALSE, theNumber);
  452.                             count = theNumber[0];
  453.                             BlockMove(&(theNumber[1]), Buffer, count);
  454.                             break;
  455.     
  456.                         case    7:    /* Hard Return */
  457.                             Buffer[0] = 13;
  458.                             break;
  459.                     }
  460.             }
  461.             
  462.             if (count)
  463.             {
  464.                 /* Boy, is TextEdit buggy !  We need to see if there is enough memory to add the textrun */
  465.                 dummyptr = NewPtr(count+TESlop);
  466.                 if (dummyptr == 0L)
  467.                     break;
  468.                 else
  469.                     DisposPtr(dummyptr);
  470.                 
  471.                 /* Translate the style to Styled TextEdit style */
  472.                 newStyle.tsFont = importPB.txtFont;
  473.                 newStyle.tsFace = (importPB.txtFace&QUICKDRAWSTYLES);
  474.                 if (newStyle.tsFace == 0) {
  475.                     aPtr = (short *)&newStyle.tsFace;    /* Fix a bug in text edit */
  476.                     *aPtr = 0;
  477.                 }
  478.                 newStyle.tsSize = importPB.txtSize;
  479.         
  480.                 RGBFromXTND(&newStyle.tsColor, importPB.txtColor);
  481.     
  482.                 TESetStyle(doFont+doSize+doFace+doColor, &newStyle, true, te);
  483.                 
  484.                 /* Now add the number of characters to the text edit handle in this window */
  485.                 
  486.                 TEInsert(Buffer, count, te);
  487.                 if (fserr = MemError())
  488.                     break;
  489.                 textrun += count;
  490.                 /* NumToString(textrun, theNumber);    */  /* Used for debugging.  Shows count in window title */
  491.                 /* SetWTitle(window, theNumber); */
  492.             }
  493.         }
  494.     
  495.         importPB.directive = importCloseMain;
  496.         (*gImportTranslator) (&importPB);
  497.     }
  498.     importPB.directive = importCloseAll;
  499.     (*gImportTranslator) (&importPB);
  500.  
  501.     TECalText(te);                                  /* calc line starts in TERecord*/
  502.     TESetSelect(0, 0, te);                            /* Set insertion point*/
  503.     AdjustScrollValues(window, true);
  504.     SetRect(&tempRect, -8000, -8000, 8000, 8000);
  505.     ClipRect(&tempRect);                        /* open clip rect so text will be drawn */
  506.  
  507.     if (resfnum)    
  508.         CloseResFile(resfnum);
  509.  
  510.     FSClose(fnum);
  511.  
  512.     (void)XTNDReleaseTranslator(pChosenOne);
  513. }
  514.  
  515.  
  516. /*----------------------------------------------------------------------*/
  517. /* ReadPlainTextFile inserts the text from the TEXT document             */
  518. /*  specified by the Standard File reply record *pTheReply into the     */
  519. /*  TextEdit record specified by hTE. The file is assumed to be         */
  520. /*  initially closed. The file is opened, the text is inserted at the     */
  521. /*  current insertion point, the window’s scrollbars are adjusted, and    */
  522. /*  the file is closed. The user is alerted if an error occurred.        */
  523. /*  Note: This version of ReadPlainTextFile() is very simplistic. It     */
  524. /*  reads the text into a block in the heap in one shot, then inserts     */
  525. /*  it into the TE record. If the free memory isn’t at least twice the     */
  526. /*  size of the text file ReadPlainTextFile() will fail. It also does     */
  527. /*  not check the current size of the text of the TE record to guard     */
  528. /*  against overflow. And it assumes that the specified file actually     */
  529. /*  exists (which it may not if the Standard File reply record was not     */
  530. /*  actually filled in by a Standard File routine).                     */
  531. /*----------------------------------------------------------------------*/
  532. static OSErr ReadPlainTextFile(const SFReply *pTheReply, TEHandle hTE)
  533. {
  534.     OSErr err;
  535.     ParamBlockRec myPB;
  536.     Handle hTx = NULL;
  537.  
  538.     /* open the text file… */
  539.     myPB.ioParam.ioNamePtr = pTheReply->fName;
  540.     myPB.ioParam.ioVRefNum = pTheReply->vRefNum;
  541.     myPB.ioParam.ioVersNum = 0;
  542.     myPB.ioParam.ioPermssn = fsRdPerm;
  543.     myPB.ioParam.ioMisc = NULL;
  544.     if ((err = PBOpen(&myPB, FALSE)) != noErr) {
  545.         AlertUser(eDocOpenFail,err);
  546.         return(err);
  547.     }
  548.     /* find out how much text in the file… */
  549.     if ((err = PBGetEOF(&myPB, FALSE)) != noErr) {
  550.         AlertUser(eDocOpenFail,err);
  551.     } else {
  552.         /* get a buffer for the text… */
  553.         if (!(hTx = NewHandle((long)myPB.ioParam.ioMisc))) {
  554.             AlertUser(eCreateFail,MemError());
  555.         } else {
  556.             MoveHHi(hTx);
  557.             HLock(hTx);
  558.             /* read the file into the buffer… */
  559.             myPB.ioParam.ioBuffer = *hTx;
  560.             myPB.ioParam.ioReqCount = (long)myPB.ioParam.ioMisc;
  561.             myPB.ioParam.ioPosMode = fsFromStart;
  562.             myPB.ioParam.ioPosOffset = 0L;
  563.             if ((err = PBRead(&myPB, FALSE)) == noErr) {
  564.                 /* insert text from buffer into TE record… */
  565.                 TEInsert(*hTx, myPB.ioParam.ioActCount, hTE);
  566.                 /* adjust window’s scrollbars… */
  567.                 AdjustScrollValues((**hTE).inPort, true);
  568.             }
  569.         }
  570.     }
  571.     if (hTx)
  572.         DisposHandle(hTx);
  573.     PBClose(&myPB, FALSE);
  574.     return(err);
  575. }
  576.  
  577.  
  578. /*----------------------------------------------------------------------*/
  579. /* SaveFile saves the file using XTND. It loads the translator,         */
  580. /*  initializes the export parameter block, and sends commands to the    */
  581. /*  translator to write out the document.  When it is finished, it        */
  582. /*  releases the translator.                                            */
  583. /*----------------------------------------------------------------------*/
  584. static    void    SaveFile(pChosenOne, theReply)
  585. TransDescribe *pChosenOne;
  586. SFReply    theReply;
  587. {
  588.     register    long    loop;
  589.     short        fserr = 0, fnum;
  590.     MatchInfo    Match;
  591.     ExportParmBlock    exportPB;
  592.     long        runlength, bufferSize;
  593.     Handle        textbuffer;
  594.     short        textface, textsize, textfont, textjust, selStart, selEnd;
  595.     char        textcolor;
  596.     Fixed        Paragraph[9];
  597.     TabSpec        tabs[20];
  598.     Point        MinusOne;
  599.     Rect        tempRect;
  600.     TEHandle    te;
  601.     WindowPtr    window = FrontWindow();
  602.     long        start, end, stylerun;
  603.     TEStyleHandle    shndl;
  604.  
  605.      te = ((DocumentPeek) window)->docTE;
  606.  
  607.     /* In order to save the document, we have to parse our own document, and determine */
  608.     /* where the paragraph and style runs start and end.  This is not a simple project in */
  609.     /* text edit! */
  610.     
  611.     /* First, let's load the Translator, just so we know we can ! */
  612.     if (fserr = XTNDLoadTranslator(pChosenOne, &gExportTranslator)) {
  613.         AlertUser(eTranslatorLoad,fserr);
  614.         return;
  615.     }
  616.  
  617.     /* Now, create the file so we can delete it.  (Takes care of PMSP problem) */
  618.     Create(theReply.fName, theReply.vRefNum, '????', '????');
  619.     if (fserr = FSDelete(theReply.fName, theReply.vRefNum)) {
  620.         /* Explain we couldn't delete the file - probably a write protect error */
  621.         AlertUser(eDeleteFailed,fserr);
  622.     } else {
  623.         Match = pChosenOne->matches[0];
  624.         if (fserr = Create(theReply.fName, theReply.vRefNum, Match.docCreator, Match.docType)) {
  625.             AlertUser(eCreateFail,fserr);
  626.         } else {
  627.             if (fserr = FSOpen(theReply.fName, theReply.vRefNum, &fnum))
  628.                 AlertUser(eOpenFail,fserr);
  629.         }
  630.     }
  631.  
  632.     if (fserr) {
  633.         (void)XTNDReleaseTranslator(pChosenOne);
  634.         return;
  635.     }
  636.  
  637.     Paragraph[0] = Paragraph[1] = Paragraph[2] = 0;        /* indent offsets */
  638.     Paragraph[3] = Paragraph[4] = Paragraph[5] = 0;        /* leading/space before/after paragraph */
  639.     Paragraph[6] = -1;                                    /* leading units (lines) */
  640.     Paragraph[7] = Paragraph[8] = 0;                    /* space before/after paragraph units (points) */
  641.     for (loop = 0; loop < 20; tabs[loop++].tabIndent = -1L);
  642.  
  643.     /* Initialize the export Translator */
  644.     SetRect(&tempRect, 0, 0, 0, 0);
  645.     MinusOne.h = MinusOne.v = -1;
  646.     textbuffer = NewHandle(256);
  647.     bufferSize = 256;
  648.     /* Check to see if this worked */
  649.  
  650.     exportPB.thePicture = nil;
  651.     exportPB.pictRect = tempRect;
  652.     exportPB.footnoteOffset = 0;
  653.     exportPB.pagePoint = MinusOne;
  654.     exportPB.datePoint = MinusOne;
  655.     exportPB.timePoint = MinusOne;
  656.  
  657.     exportPB.textBuffer = textbuffer;
  658.     exportPB.textLength = &runlength;
  659.     exportPB.result = &fserr;
  660.     exportPB.refNum = &fnum;
  661.     exportPB.txtFace = &textface;
  662.     exportPB.txtSize = &textsize;
  663.     exportPB.txtFont = &textfont;
  664.     exportPB.txtColor = &textcolor;
  665.     exportPB.txtJust = &textjust;
  666.     exportPB.paraFmts = Paragraph;
  667.     exportPB.tabs = tabs;
  668.     exportPB.footnoteText = nil;
  669.  
  670.     exportPB.topMargin = 0x00480000;        /* 1 inch margin */
  671.     exportPB.bottomMargin = 0x00480000;        /* 1 inch margin */
  672.     exportPB.leftMargin = 0x00480000;        /* 1 inch margin */
  673.     exportPB.rightMargin = 0x00480000;        /* 1 inch margin */
  674.     exportPB.gutter = 0x000C0000;        /* 12 point column gap */
  675.     exportPB.numCols = 1;
  676.     exportPB.startPageNum = 1;
  677.     exportPB.startFootnoteNum = 1;
  678.     exportPB.currentStory = mainStory;
  679.     exportPB.rulerShowing = TRUE;
  680.     exportPB.doubleSided = FALSE;
  681.     exportPB.titlePage = FALSE;
  682.     exportPB.endnotes = FALSE;
  683.     exportPB.showInvisibles = TRUE;
  684.     exportPB.showPageGuides = TRUE;
  685.     exportPB.showPictures = TRUE;
  686.     exportPB.autoFootnotes = TRUE;
  687.     exportPB.smartQuotes = TRUE;
  688.     exportPB.fractCharWidths = TRUE;
  689.     exportPB.hRes = 72;
  690.     exportPB.vRes = 72;
  691.     exportPB.windowRect = tempRect;
  692.  
  693.     exportPB.headerStatus = 0;
  694.     exportPB.footerStatus = 0;
  695.     exportPB.totalCharCount = end = (**te).teLength;
  696.     exportPB.footnotesExist = FALSE;
  697.  
  698.     exportPB.theReply = theReply;
  699.     exportPB.thisTranslator = *pChosenOne;
  700.  
  701.     selStart = (**te).selStart;
  702.     selEnd = (**te).selEnd;
  703.     SetRect(&tempRect, 0, 0, 0, 0);
  704.     ClipRect(&tempRect);                        /* close clip rect so text will not be drawn */
  705.  
  706.     PrOpen();
  707.     if (!PrError()) {
  708.         exportPB.printRecord = (THPrint)NewHandle(sizeof(TPrint));
  709.         if (exportPB.printRecord) {
  710.             PrintDefault(exportPB.printRecord);
  711.             PrValidate(exportPB.printRecord);
  712.         }
  713.         PrClose();
  714.     } else
  715.         exportPB.printRecord = 0L;
  716.  
  717.  
  718.     exportPB.directive = exportInitAll;
  719.     (*gExportTranslator) (&exportPB);
  720.     
  721.     /* OK - let's open the main story */
  722.     
  723.     exportPB.directive = exportOpenMain;
  724.     exportPB.currentStory = mainStory;
  725.     (*gExportTranslator) (&exportPB);
  726.     if (!(*exportPB.result)) {
  727.  
  728.         shndl = GetStylHandle(te);    /* There may not be _any_ style runs. */
  729.         if (shndl) {
  730.             StyleRun    *sruns;
  731.             Handle        theText;
  732.             long        length, offset, textLength;
  733.             STElement    *styleruns, Anentry;
  734.             char        *TextPtr;
  735.             STHandle    thestyles;
  736.         
  737.             theText = (Handle) TEGetText(te);
  738.             textLength = (**te).teLength;
  739.             HLock(theText);
  740.             TextPtr = *theText;
  741.             HLock((Handle)shndl);
  742.             sruns = (**shndl).runs;
  743.             thestyles = (**shndl).styleTab;
  744.             HLock((Handle)thestyles);
  745.             styleruns = *thestyles;
  746.             for (stylerun = 0; stylerun < (**shndl).nRuns; stylerun++, sruns++) {
  747.                 start = sruns->startChar;
  748.                 length = (sruns + 1)->startChar - start;
  749.                 if (length + start > textLength) 
  750.                     length = textLength - start;
  751.                 runlength = offset = 0;
  752.                 /* Find the associated style entry */
  753.                 Anentry = (styleruns)[sruns->styleIndex];
  754.                 textface = Anentry.stFace;
  755.                 textsize = Anentry.stSize << 2;    /* multiply by four to simulate MacWrite II font size */
  756.                 textcolor = RGBToXTND(Anentry.stColor);
  757.                 textfont = Anentry.stFont;
  758.                 textjust = 0;    /* left; */
  759.                 while (offset + runlength < length) {
  760.                     while (offset + runlength < length)
  761.                         if (TextPtr[runlength++] == 13)
  762.                             break;
  763.                     /* Send runlength characters, starting at start + offset */
  764.                     SetHandleSize(textbuffer, runlength);
  765.                     /* check to see if this fails */
  766.                     if (runlength > bufferSize)
  767.                         SetHandleSize(textbuffer, bufferSize = runlength);
  768.                     BlockMove(TextPtr, *textbuffer, runlength);
  769.                     exportPB.directive = exportWriteText;
  770.                     (*gExportTranslator) (&exportPB);
  771.                     TextPtr += runlength;
  772.                     TESetSelect(start + offset, start + offset + runlength, te);
  773.                     offset += runlength;
  774.                     runlength = 0;
  775.                 }
  776.             }
  777.         }
  778.         exportPB.directive = exportCloseMain;
  779.         (*gExportTranslator) (&exportPB);
  780.     }
  781.     exportPB.directive = exportCloseAll;
  782.     (*gExportTranslator) (&exportPB);
  783.     
  784.     if (exportPB.printRecord)
  785.         DisposHandle( (Handle) exportPB.printRecord);
  786.     FSClose(fnum);
  787.     
  788.     /* Write resource fork now. */
  789.     {
  790.         Str255    vName;
  791.         short    vRefNum;
  792.     
  793.         GetVol(vName, &vRefNum);
  794.         SetVol(0L, theReply.vRefNum);
  795.         CreateResFile(theReply.fName);
  796.         fnum = OpenResFile(theReply.fName);
  797.         exportPB.directive = exportWriteResources;
  798.         (*gExportTranslator) (&exportPB);
  799.         CloseResFile(fnum);
  800.         SetVol(0L, vRefNum);
  801.     }
  802.  
  803.     (void)XTNDReleaseTranslator(pChosenOne);
  804.  
  805.     TESetSelect(selStart, selEnd, te);            /*Set insertion point*/
  806.     SetRect(&tempRect, -8000, -8000, 8000, 8000);
  807.     ClipRect(&tempRect);                        /* open clip rect so text will be drawn */
  808. }
  809.  
  810.  
  811. /*----------------------------------------------------------------------*/
  812. /* SavePlainTextFile saves the text from the TextEdit record specified    */
  813. /*  by hTE to the file having the name fileName on the volume specified    */
  814. /*  by vRefNum and in the directory specified by dirID. The file is     */
  815. /*  assumed to be initially closed. It is opened, the text written out    */
  816. /*  (replacing any previous contents of the file), and the file is         */
  817. /*  closed. If saveAll is TRUE the entire text of the edit record is     */
  818. /*  written out, otherwise only the text within the current selection     */
  819. /*  is saved. The user is alerted if an error occured.                     */
  820. /*----------------------------------------------------------------------*/
  821. static OSErr SavePlainTextFile(Str255 fileName, short vRefNum, long dirID,
  822.     TEHandle hTE, Boolean saveAll)
  823. {
  824.     OSErr err;
  825.     SignedByte mfb;
  826.     HParamBlockRec myHPB;
  827.  
  828.     /* open the file… */
  829.     myHPB.ioParam.ioNamePtr = fileName;
  830.     myHPB.ioParam.ioVRefNum = vRefNum;
  831.     myHPB.ioParam.ioVersNum = 0;
  832.     myHPB.ioParam.ioPermssn = fsRdWrPerm;
  833.     myHPB.ioParam.ioMisc = NULL;
  834.     myHPB.fileParam.ioDirID = dirID;
  835.     if ((err = PBHOpen(&myHPB, FALSE)) != noErr) {
  836.         AlertUser(eOpenFail,err);
  837.         return(err);
  838.     }
  839.     /* reset eof of file to zero… */
  840.     myHPB.ioParam.ioMisc = 0L;
  841.     if ((err = PBSetEOF((ParmBlkPtr)&myHPB, FALSE)) != noErr) {
  842.         AlertUser(eOpenFail,err);
  843.     } else {
  844.         /* temporarily lock down text block of TE record;
  845.            this is paranoia since PBWrite() is not supposed to move/purge memory… */
  846.         MoveHHi((**hTE).hText);
  847.         mfb = HGetState((**hTE).hText);
  848.         HLock((**hTE).hText);
  849.         /* write out TE text to file & reset lock-state of text block… */
  850.         myHPB.ioParam.ioBuffer = *(**hTE).hText + ((saveAll) ? 0 : (**hTE).selStart);
  851.         myHPB.ioParam.ioReqCount = (saveAll) ? (**hTE).teLength : (**hTE).selEnd - (**hTE).selStart;
  852.         myHPB.ioParam.ioPosMode = fsFromStart;
  853.         myHPB.ioParam.ioPosOffset = 0L;
  854.         err = PBWrite((ParmBlkPtr)&myHPB, FALSE);
  855.         HSetState((**hTE).hText, mfb);
  856.     }
  857.     (void)PBClose((ParmBlkPtr)&myHPB, FALSE);
  858.     return(err);
  859. }
  860.  
  861.  
  862. /*----------------------------------------------------------------------*/
  863. /* SaveNewPlainTextFile creates a new file and saves the text from the     */
  864. /*  TextEdit record specified by hTE. The file is created with a         */
  865. /*  creator of 'XTND' and filetype fileType. The name and location of    */
  866. /*  the file is specified by the Standard File reply record *pTheReply.    */
  867. /*  The directory ID and real volume reference number of the specified    */
  868. /*  location are returned through the VAR parameters *pVRefNum and         */
  869. /*  *pDirID. Any existing file is first deleted. The file is closed     */
  870. /*  after saving its contents. If saveAll is TRUE the entire text of     */
  871. /*  the edit record is written out, otherwise only the text within the    */
  872. /*  current selection is saved. If an error occured the user is alerted    */
  873. /*  and the file is deleted.                                             */
  874. /*----------------------------------------------------------------------*/
  875. static OSErr SaveNewPlainTextFile(const SFReply *pTheReply, OSType fileType,
  876.     TEHandle hTE, Boolean saveAll, short *pVRefNum, long *pDirID)
  877. {
  878.     OSErr err;
  879.     WDPBRec myWDPB;
  880.     HParamBlockRec myHPB;
  881.  
  882.     /* get the dirID and real vRefNum… */
  883.     myWDPB.ioNamePtr = NULL;
  884.     myWDPB.ioVRefNum = pTheReply->vRefNum;
  885.     myWDPB.ioWDIndex = 0;
  886.     myWDPB.ioWDProcID = 0L;
  887.     if ((err = PBGetWDInfo(&myWDPB, FALSE)) != noErr) {
  888.         AlertUser(eCreateFail,err);
  889.         return(err);
  890.     }
  891.     /* delete file (if it already exists)… */
  892.     myHPB.ioParam.ioNamePtr = pTheReply->fName;
  893.     myHPB.ioParam.ioVRefNum = *pVRefNum = myWDPB.ioWDVRefNum;
  894.     myHPB.ioParam.ioVersNum = 0;
  895.     myHPB.fileParam.ioDirID = *pDirID = myWDPB.ioWDDirID;
  896.     if ((err = PBHDelete(&myHPB, FALSE)) != noErr && err != fnfErr) {
  897.         /* …possibly locked or busy, or who knows what */
  898.         AlertUser(eDeleteFailed,err);
  899.         return(err);
  900.     }
  901.     /* create the file… */
  902.     if ((err = PBHCreate(&myHPB, FALSE)) != noErr) {
  903.         AlertUser(eCreateFail,err);
  904.         return(err);
  905.     }
  906.     /* write text to file… */
  907.     if (!(err = SavePlainTextFile(pTheReply->fName, myWDPB.ioWDVRefNum, myWDPB.ioWDDirID, hTE, saveAll)))
  908.     {
  909.         /* set filetype & creator information… */
  910.         myHPB.fileParam.ioFDirIndex = 0;
  911.         if ((err = PBHGetFInfo(&myHPB, FALSE)) == noErr) {
  912.             myHPB.fileParam.ioDirID = myWDPB.ioWDDirID;
  913.             myHPB.fileParam.ioFlFndrInfo.fdType = fileType;
  914.             myHPB.fileParam.ioFlFndrInfo.fdCreator = 'XTND';
  915.             err = PBHSetFInfo(&myHPB, FALSE);
  916.         }
  917.     }
  918.     if (err != noErr) {
  919.         AlertUser(eCreateFail,err);
  920.         (void)PBHDelete(&myHPB, FALSE);
  921.     }
  922.     return(err);
  923. }
  924.  
  925.  
  926. /*----------------------------------------------------------------------*/
  927. /* GetNextStory will send the appropriate directive to the translator    */
  928. /*  to request the next story depending on the current story.            */
  929. /*----------------------------------------------------------------------*/
  930. static void Getnextstory(thePtr)
  931. register ImportParmBlkPtr    thePtr;
  932.  
  933. {
  934.     while (thePtr->directive != importAcknowledge)
  935.     {
  936.  
  937.         (*gImportTranslator)(thePtr);
  938.  
  939.         switch(thePtr->directive)
  940.         {
  941.             default:
  942.                 thePtr->directive = importAcknowledge;
  943.                 break;
  944.  
  945.             case importInitRightHeader:
  946.                 thePtr->currentStory = leftHeaderStory;
  947.                 thePtr->directive = importInitLeftHeader;
  948.                 break;
  949.                 
  950.             case importInitLeftHeader:
  951.                 thePtr->currentStory = headerStory;
  952.                 thePtr->directive = importInitHeader;
  953.                 break;
  954.                 
  955.             case importInitHeader:
  956.                 thePtr->currentStory = rightFooterStory;
  957.                 thePtr->directive = importInitRightFooter;
  958.                 break;
  959.                 
  960.             case importInitRightFooter:
  961.                 thePtr->currentStory = leftFooterStory;
  962.                 thePtr->directive = importInitLeftFooter;
  963.                 break;
  964.                 
  965.             case importInitLeftFooter:
  966.                 thePtr->currentStory = footerStory;
  967.                 thePtr->directive = importInitFooter;
  968.                 break;
  969.                 
  970.             case importInitFooter:
  971.                 thePtr->currentStory = mainStory;
  972.                 thePtr->directive = importInitMain;
  973.                 break;
  974.         }
  975.     }
  976. }
  977.  
  978.  
  979. /*----------------------------------------------------------------------*/
  980. /* RGBToXTND converts a RGB color to the corresponding XTND color         */
  981. /*  value, which, although it is in the range from 0 to 7, is NOT the    */
  982. /*  same as the QuickDraw color values.                                    */
  983. /*----------------------------------------------------------------------*/
  984. short    RGBToXTND(theColor)
  985. RGBColor    theColor;
  986. {
  987.     register    short    r,g,b;
  988.     /* ColorMap contains the conversion from QuickDraw color to our color id */
  989.     static short colormap[8] = {1,4,3,5,2,6,7,0};
  990.  
  991.     r = theColor.red & 0x8000 ? 4:0;
  992.     g = theColor.green & 0x8000 ? 2:0;
  993.     b = theColor.blue & 0x8000 ? 1:0;
  994.     return (colormap[r+g+b]);
  995. }
  996.  
  997.  
  998. /*----------------------------------------------------------------------*/
  999. /* RGBFromXTND an XTND color value to the corresponding RGB color          */
  1000. /*  value.  XTND color values are NOT the same as QuickDraw colors.        */
  1001. /*----------------------------------------------------------------------*/
  1002. void    RGBFromXTND(rgb, colorcode)
  1003. RGBColor    *rgb;
  1004. short        colorcode;
  1005. {
  1006.     switch(colorcode)
  1007.     {
  1008.         case    0:    /* WHITE */
  1009.             rgb->red = rgb->green = rgb->blue = 65535; break;
  1010.         case    1:    /* BLACK */
  1011.             rgb->red = rgb->green = rgb->blue = 0; break;
  1012.         case    2:    /* RED */
  1013.             rgb->red = 65535; rgb->green = rgb->blue = 0; break;
  1014.         case    3:    /* GREEN */
  1015.             rgb->red = rgb->blue = 0; rgb->green = 65535; break;
  1016.         case    4:    /* BLUE */
  1017.             rgb->red = rgb->green = 0; rgb->blue = 65535; break;
  1018.         case    5:    /* CYAN */
  1019.             rgb->red = 0; rgb->green = rgb->blue = 65535; break;
  1020.         case    6:    /* MAGENTA */
  1021.             rgb->red = rgb->blue = 65535; rgb->green = 0; break;
  1022.         case    7:    /* YELLOW */
  1023.             rgb->red = rgb->green = 65535; rgb->blue = 0; break;
  1024.     }
  1025. }
  1026.  
  1027. /*------------------------------------------------------------------------------*/
  1028. void ZeroBlock(block, size)
  1029. register void *block;
  1030. register long size;
  1031. {
  1032.     register Ptr    blockPtr;
  1033.     register long     *longPtr;
  1034.  
  1035.     blockPtr = block;
  1036.     
  1037.     while (size & 3)                /* while (size % sizeof(long)) */
  1038.         blockPtr[--size] = 0;
  1039.     
  1040.     if (!size) return;
  1041.  
  1042.     longPtr = (long *) block;
  1043.     size >>= 2;                        /* size /= sizeof(long) */
  1044.  
  1045.     while (size--)
  1046.         *longPtr++ = 0L;
  1047. } /* ZeroBlock */
  1048.  
  1049.